#include "TutVideo1.h"
#include <QDebug>

namespace TDigitalSignage
{
	/**
	* Constructor
	*/
	TutVideo1::TutVideo1() :
		Interactable(),
		mVideo1(),
		mTouchList(),		
		mApplicationDirectory(),
		mIsLoaded(false),
		mLooping(false),
		mPaused(true),
		mGUIManager(),
		mpBigPlay(NULL),
		mpPlayPause(NULL),
		mpTimeSlider(NULL),
		mpLoop(NULL),
		mpVolume(NULL),
		mpVolumeSlider(NULL)
	{
	}

	/**
	* Destructor
	*/
	TutVideo1::~TutVideo1()
	{
	}

	
	/**
	* Loads the video and also loads gui graphics
	* \param inApplicationDirectory Path to the applications data directory
	* \param inFilePath Path to the Video file
	*/
	bool TutVideo1::load(const QString& inApplicationDirectory, const QString& inFilePath)
	{
		mApplicationDirectory = inApplicationDirectory;

		// load the video
		mVideo1.load(inFilePath);

		// Listen to the GUI events
		mGUIManager.addListener(this);

		return true;
	}
	
	/**
	* unload all initialized resources
	*/
	void TutVideo1::unload()
	{
		mVideo1.unload();
		mGUIManager.unload();
	}
	
	/**
	* onUpdate event handler
	* \param inFrameTime elapsed time in seconds since the last frame
	*/
	void TutVideo1::onUpdate(float inFrameTime)
	{
		// update the interactable parent class parameters
		Interactable::update(inFrameTime);

		// check if the video resources has completed initialization. If yes then setup the object
		if(!mIsLoaded)
		{
			if(mVideo1.isLoaded())
			{
				mIsLoaded = true;
				mLooping = false;	
				mVideo1.setLoop(mLooping);
				mVideo1.setSpeed(1.f);
				mVideo1.setVolume(256);
				mVideo1.setPosition(0.f);
				

				loadGUI(mApplicationDirectory);
				//pauseVideo();
			}
		}

		// if video is not loaded then exit
		if(!mIsLoaded)
			return;

		// Update the video
		mVideo1.update();

		// Update the gui
		mGUIManager.update(inFrameTime);
		repositionGUI();

		playVideo();

		// Update GUI controls
		// If looping is not enabled, then rewind the video once playback is complete
		if(mVideo1.getPosition() >= 1 && !mLooping)
		{
			resetVideo();
			mpPlayPause->setChecked(false);
		}

		// Move the timer slider nub based on value of the video player time
		mpTimeSlider->setValue(mVideo1.getPosition());
	}
	
	/**
	* Draw the video and controls
	*/
	void TutVideo1::onRender()
	{
		if(!mIsLoaded)
			return;

		// render the video texture in a quad
		glPushMatrix();
		
		glEnable(GL_BLEND);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_TEXTURE_2D);

		glTranslatef(getPosition().x(), getPosition().y(), 0);
		//glRotatef(Blobz::radToDeg(getRotation()), 0, 0, 1);
		glScalef(getScale(), getScale(), 1.0f);
		
		QVector2D moviePos(mVideo1.getAspect()*0.67f, 0.715f);
		QVector2D size(mVideo1.getAspect()/1.775f, 0.53);

		float verts[8] = {	moviePos.x(), moviePos.y(), 
							moviePos.x() + size.x(), moviePos.y(),
							moviePos.x() + size.x(), moviePos.y() + size.y(),
							moviePos.x(), moviePos.y() + size.y() };

		static float texCoords[8] = {	0, 1,
										1, 1,
										1, 0,
										0, 0 };

		glBindTexture(GL_TEXTURE_2D, mVideo1.getTextureID());

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		glVertexPointer(2, GL_FLOAT, 0, verts);
		glTexCoordPointer(2, GL_FLOAT, 0, texCoords);

		glDrawArrays(GL_QUADS, 0, 4);

		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);

		glDisable(GL_TEXTURE_2D);
		glPopMatrix();

		//render the overlay gui
		//mGUIManager.render();
	}

	/**
	* Finger down event handler
	* \param inData holds the touch information
	*/
	void TutVideo1::onFingerDown(const TouchListener::TouchData& inData)
	{
		if(!mIsLoaded)
			return;

		// mGUIManager.onFingerDown(inData) returns true if the touch event was handled by the gui
		if(mGUIManager.onFingerDown(inData))
			return;

		// Check if the touch lies within the video area
		if(isInside(inData.toVector2D()))
		{
			// if the touch lies within the video area, store the touch id in a list. 
			// This technique is called touch grabbing
			mTouchList.insert(inData.mID);

			// pass the touch event to update the parent's class parameters
			Interactable::fingerDown(inData);
		}		
	}

	/**
	* Finger up event handler
	* \param inData holds the touch information
	*/
	void TutVideo1::onFingerUp(const TouchListener::TouchData& inData)
	{
		if(!mIsLoaded)
			return;

		// mGUIManager.onFingerDown(inData) returns true if the touch event was handled by the gui
		if(mGUIManager.onFingerUp(inData))
			return;

		// Check if the touchId is present in the internal touch id list
		if(mTouchList.findID(inData.mID))
		{
			// Released the previously grabbed touch from the touch list
			mTouchList.removeID(inData.mID);

			// pass the touch event to update the parent's class parameters
			Interactable::fingerUp(inData);
		}		
	}

	/**
	* Finger update event handler
	* \param inData holds the touch information
	*/
	void TutVideo1::onFingerUpdate(const TouchListener::TouchData& inData)
	{
		if(!mIsLoaded)
			return;

		// mGUIManager.onFingerUpdate(inData) returns true if the touch event was handled by the gui
		if(mGUIManager.onFingerUpdate(inData))
			return;

		// Check if the touchId is present in the internal touch id list
		if(mTouchList.findID(inData.mID))
		{
			// pass the touch event to update the parent's class parameters
			Interactable::fingerUpdate(inData);
		}		
	}

	/**
	* Triggered when there is a GUI event from the GUIManager that the class is listening to
	* \param inpComponent A pointer to the GUI component
	* \param inGUIEvent The GUI event type
	* \param inFingerEvent The finger event (up/down/update) associated with the GUI event
	*/
	void TutVideo1::onGUIEvent( Blobz::GUIBase* inpComponent, Blobz::GUIEvent inGUIEvent, Blobz::FingerEvent inFingerEvent )
	{
		// Check if the event is caused by a button being released
		if(inGUIEvent == Blobz::Button_Released)
		{
			// compare the GUI component pointer to our button pointers
			if(inpComponent == mpBigPlay)
			{
				// play the video
				playVideo();
				// toggle the mini play button state to pause button state
				mpPlayPause->setChecked(true);
			}
			else if(inpComponent == mpVolume)
			{
				// show/hide the volume slider
				if(mpVolumeSlider->isVisible())
					mpVolumeSlider->setVisible(false);
				else
					mpVolumeSlider->setVisible(true);
			}
		}
		else if(inpComponent == mpPlayPause)
		{
			if(inGUIEvent == Blobz::CheckBox_Checked)
			{
				// play video
				playVideo();
			}
			else
			{
				// pause video
				pauseVideo();
			}
		}
		else if(inpComponent == mpLoop)
		{
			if(inGUIEvent == Blobz::CheckBox_Checked)
			{
				// do looping
				mLooping = true;
			}
			else
			{
				// do not do looping
				mLooping = false;
			}
			mVideo1.setLoop(mLooping);			
		}
		else if(inGUIEvent == Blobz::Slider_ValueChange)
		{
			if(inpComponent == mpTimeSlider)
			{
				// pause the video first
				pauseVideo();
				// fetch the slider valure and set the position of the video with it.
				mVideo1.setPosition(((Blobz::Slider*)inpComponent)->getValue());
			}
			else if(inpComponent == mpVolumeSlider)
			{
				// fetch the value from the volume slider and set the volume of the video with it
				mVideo1.setVolume(((Blobz::Slider*)inpComponent)->getValue());
			}
		}
	}

	/**
	* Loads the gui graphics resources
	* \param inAppDir Path to the application's data directory
	*/
	void TutVideo1::loadGUI( const QString& inAppDir )
	{
		// load big play button
		mpBigPlay = mGUIManager.addButton("BigPlayButton", inAppDir+"play_unpressed.png", inAppDir+"play_pressed.png", "");

		// load mini play button
		mpPlayPause = mGUIManager.addCheckBox("MiniPlayButton", inAppDir+"small_play.png", inAppDir+"small_pause.png");		

		// load time slider
		mpTimeSlider = mGUIManager.addSlider("TimeSlider", inAppDir+"sliderbar.png", inAppDir+"slider_unselected.png", inAppDir+"slider_selected.png");
		mpTimeSlider->setMinMax(0,1);

		// load loop button
		mpLoop = mGUIManager.addCheckBox("MiniLoopButton", inAppDir+"small_Loop.png", inAppDir+"small_Loop02.png");

		// load volume button
		mpVolume = mGUIManager.addButton("MiniVolume", inAppDir+"small_unmute.png", inAppDir+"small_unmute.png", "");

		// load volume slider button
		mpVolumeSlider = mGUIManager.addSlider("VolumeSlider", inAppDir+"sliderbar.png", inAppDir+"slider_unselected.png", inAppDir+"slider_selected.png");
		mpVolumeSlider->setMinMax(0,256);
		mpVolumeSlider->setValue(256);
		mpVolumeSlider->setVisible(false);
	}

	/**
	* Updates the scaling, position and rotation information of gui elements
	*/
	void TutVideo1::repositionGUI()
	{
		QVector2D vidPos = getPosition();
		QVector2D vidSize = QVector2D(mVideo1.getAspect(), 1.f)*getScale();
		QVector2D vidBottomLeft = vidPos - vidSize/2.f;

		//Big Play button settings
		QVector2D bigPlaySize = QVector2D(vidSize.y(), vidSize.y())*0.35f;
		mpBigPlay->setPosition(vidPos);
		mpBigPlay->setSize(bigPlaySize);
		mpBigPlay->setRotation(getRotation());

		//default menu height
		float menuHeight = 0.1f;
		QVector2D menuButtonSize = QVector2D(1.7f, 1.f) * menuHeight * getScale();

		//Mini Play button settings
		QVector2D initMiniPlayPos = vidBottomLeft + QVector2D(menuButtonSize.x()/2.f, -menuButtonSize.y()/2.f);
		QVector2D currMiniPlayPos = rotatePointAboutPoint(initMiniPlayPos, vidPos, getRotation());
		mpPlayPause->setPosition(currMiniPlayPos);
		mpPlayPause->setSize(menuButtonSize);
		mpPlayPause->setRotation(getRotation());
		mpPlayPause->setColor(1,1,1);

		//Time Slider Settings
		QVector2D timeSliderSize = QVector2D(vidSize.x()-menuButtonSize.x()*3.f, menuButtonSize.y());
		QVector2D initTimeSliderPos = initMiniPlayPos + QVector2D(menuButtonSize.x()/2.f + timeSliderSize.x()/2.f, 0.f);
		QVector2D currTimeSliderPos = rotatePointAboutPoint(initTimeSliderPos, vidPos, getRotation());
		mpTimeSlider->setPosition(currTimeSliderPos);
		mpTimeSlider->setSize(timeSliderSize);
		mpTimeSlider->setRotation(getRotation());

		//Mini Loop button settings
		QVector2D initLoopPos = initTimeSliderPos + QVector2D(timeSliderSize.x()/2.f + menuButtonSize.x()/2.f, 0.f);
		QVector2D currLoopPos = rotatePointAboutPoint(initLoopPos, vidPos, getRotation());
		mpLoop->setPosition(currLoopPos);
		mpLoop->setSize(menuButtonSize);
		mpLoop->setRotation(getRotation());
		mpLoop->setColor(1,1,1);

		//Volume button settings
		QVector2D initVolumePos = initLoopPos + QVector2D(menuButtonSize.x(), 0.f);
		QVector2D currVolumePos = rotatePointAboutPoint(initVolumePos, vidPos, getRotation());
		mpVolume->setPosition(currVolumePos);
		mpVolume->setSize(menuButtonSize);
		mpVolume->setRotation(getRotation());

		//Volume Slider settings
		QVector2D volSliderSize = QVector2D(menuButtonSize.x()*5.f, menuButtonSize.x());
		QVector2D initVolSliderPos = initVolumePos + QVector2D(0.f, menuButtonSize.y()/2.f+volSliderSize.x()/2.f);
		QVector2D currVolSliderPos = rotatePointAboutPoint(initVolSliderPos, vidPos, getRotation());
		mpVolumeSlider->setPosition(currVolSliderPos);
		mpVolumeSlider->setSize(volSliderSize);
		mpVolumeSlider->setRotation(getRotation() + Blobz::PI*0.5f);
	}

	/**
	* Rotates point A about point B
	* \param inPointA Point A Coordinates
	* \param inPointB Point B Coordinates
	* \param inAngle Rotation Angle in radians
	* \return Rotated point
	*/
	QVector2D TutVideo1::rotatePointAboutPoint(QVector2D inPointA, QVector2D inPointB, float inAngle )
	{
		QVector2D tPos = inPointA - inPointB;
		float xNew = tPos.x()*cos(inAngle) - tPos.y()*sin(inAngle);
		float yNew = tPos.x()*sin(inAngle) + tPos.y()*cos(inAngle);
		tPos = QVector2D(xNew, yNew);
		tPos = tPos + inPointB;
		return tPos;
	}

	/**
	* Checks if the point is within the video player area
	* \param inPoint Point to be checked
	* \return bool Returns true on point is inside the video rectangle, false otherwise
	*/
	bool TutVideo1::isInside( QVector2D inPoint )
	{
		float menuHeight = 0.1f;

		return Blobz::pointInOrientedRectangle(inPoint, getPosition() - QVector2D(0, menuHeight*0.5f*getScale()), QVector2D(mVideo1.getAspect(), 1.f + menuHeight)*getScale(), getRotation());
	}

	/**
	* Plays the video and sets the gui appropriately
	*/
	void TutVideo1::playVideo()
	{
		mPaused = false;
		mVideo1.setPaused(false);
		mpBigPlay->setVisible(false);
	}

	/**
	* Pause the video and sets the gui appropriately
	*/
	void TutVideo1::pauseVideo()
	{
		mPaused = true;
		mVideo1.setPaused(true);		
		mpBigPlay->setVisible(true);
	}

	/**
	* Resets the video
	*/
	void TutVideo1::resetVideo()
	{
		mVideo1.setPosition(0);
		pauseVideo();
	}
}